import warnings
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.linear_model.coordinate_descent import ConvergenceWarning
from sklearn.linear_model import LogisticRegressionCV
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler

## 设置字符集，防止中文乱码
mpl.rcParams['font.sans-serif']=[u'simHei']
mpl.rcParams['axes.unicode_minus']=False

## 拦截异常
warnings.filterwarnings(action='ignore',category=ConvergenceWarning)

## 自变量名称
columns_x = ["fixed acidity", "volatile acidity", "citric acid",
             "residual sugar", "chlorides", "free sulfur dioxide",
             "total sulfur dioxide", "density", "pH", "sulphates",
             "alcohol", "type"]

## 因变量名称
columns_y="quality"

def run_algorithm(df):
    ## 读取数据
    path_1 = '../../data/winequality-red.csv'
    df_1 = pd.read_csv(path_1, sep=';')
    df_1['type'] = 1  # 设置数据类型为红葡萄酒

    path_2 = '../../data/winequality-white.csv'
    df_2 = pd.read_csv(path_2, sep=';')
    df_2['type'] = 2

    # 合并两个df
    df_3 = pd.concat([df_1, df_2], axis=0)

    ## 异常数据处理
    df_4 = df_3.replace('?', np.nan).dropna(how='any')  # 只要有列为空，就进行删除操作
    print("原始数据条数:%d；异常数据处理后数据条数:%d；异常数据条数:%d" % (len(df_3), len(df_4), len(df_3) - len(df_4)))

    datas=df_4
    ## 提取自变量和因变量
    X=datas[columns_x]
    Y=datas[columns_y]

    ## 数据分割
    X_train,X_test,Y_train,Y_test=train_test_split(X,Y,test_size=0.25,random_state=0)
    print("训练数据条数:%d；数据特征个数:%d；测试数据条数:%d" % (X_train.shape[0], X_train.shape[1], X_test.shape[0]))

    # 2. 数据格式化(归一化)
    mms=MinMaxScaler()
    X_train=mms.fit_transform(X_train)
    ## 查看y值的范围和数理
    print(Y_train.value_counts())

    # 3. 模型构建及训练
    ## penalty: 过拟合解决参数,l1或者l2

    ## solver: 参数优化方式
    ### 当penalty为l1的时候，参数只能是：liblinear(坐标轴下降法)；
    ### 当penalty为l2的时候，参数可以是：lbfgs(拟牛顿法)、newton-cg(牛顿法变种)

    ## multi_class: 分类方式参数；参数可选: ovr(默认)、multinomial；这两种方式在二元分类问题中，效果是一样的；在多元分类问题中，效果不一样
    ### ovr: one-vs-rest， 对于多元分类的问题，先将其看做二元分类，分类完成后，再迭代对其中一类继续进行二元分类
    ### multinomial: many-vs-many（MVM）,对于多元分类问题，如果模型有T类，我们每次在所有的T类样本里面选择两类样本出来，
    #### 不妨记为T1类和T2类，把所有的输出为T1和T2的样本放在一起，把T1作为正例，T2作为负例，
    #### 进行二元逻辑回归，得到模型参数。我们一共需要T(T-1)/2次分类

    ## class_weight: 特征权重参数

    ### Softmax算法相对于Logistic算法来讲，在sklearn中体现的代码形式来讲，主要只是参数的不同而已
    ## Logistic算法回归(二分类): 使用的是ovr；如果是softmax回归，建议使用multinomial
    lr=LogisticRegressionCV(fit_intercept=True,Cs=np.logspace(-5,1,100),multi_class='multinomial',penalty='l2',solver='lbfgs')
    lr.fit(X_train,Y_train)
    # 4. 模型效果获取
    r=lr.score(X_train,Y_train)
    print("R值：", r)
    print("特征稀疏化比率：%.2f%%" % (np.mean(lr.coef_.ravel()==0)*100))
    print("参数：",lr.coef_)
    print("截距：",lr.intercept_)

    # 数据预测
    ## a. 预测数据格式化(归一化)
    X_test = mms.transform(X_test) # 使用模型进行归一化操作
    ## b. 结果数据预测
    Y_predict=lr.predict(X_test)
    ## c. 图表展示
    len_x=range(len(X_test))
    plt.figure(figsize=(14,7),facecolor='w')
    plt.ylim(-1,11)
    plt.plot(len_x,Y_test,'ro',markersize=8,zorder=3,label=u'真实值')
    plt.plot(len_x,Y_predict,'go',markersize=12,zorder=2,label=u'预测值,R^2=%.3f' %(lr.score(X_test,Y_test)))
    plt.legend(loc='upper left')
    plt.xlabel(u'数据编号',fontsize=18)
    plt.ylabel(u'葡萄酒质量',fontsize=18)
    plt.title(u'葡萄酒质量预测统计',fontsize=20)
    plt.grid(b=True)
    plt.show()

    # 查看数据分布情况
    print([len(df[df.quality==i]) for i in range(11)])


run_algorithm()


